package com.xinzhidi.web.dataset.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinzhidi.common.constant.AgreementConstants;
import com.xinzhidi.common.core.domain.model.LoginUser;
import com.xinzhidi.common.core.page.TableDataInfo;
import com.xinzhidi.common.exception.ServiceException;
import com.xinzhidi.common.utils.DateUtils;
import com.xinzhidi.common.utils.SecurityUtils;
import com.xinzhidi.common.utils.StringUtils;
import com.xinzhidi.common.utils.bean.BeanUtils;
import com.xinzhidi.web.alarmSetting.domain.dto.AlarmSettingListDto;
import com.xinzhidi.web.alarmSetting.domain.vo.AlarmSettingListVo;
import com.xinzhidi.web.alarmSetting.mapper.AlarmSettingMapper;
import com.xinzhidi.web.build.domain.BuildUnit;
import com.xinzhidi.web.build.domain.CBuild;
import com.xinzhidi.web.build.service.IBuildUnitService;
import com.xinzhidi.web.build.service.ICBuildService;
import com.xinzhidi.web.controlset.domain.ControlSet;
import com.xinzhidi.web.controlset.service.ControlSetService;
import com.xinzhidi.web.dataset.domain.DataSet;
import com.xinzhidi.web.dataset.domain.dto.DataSetDto;
import com.xinzhidi.web.dataset.domain.dto.DataSetListDto;
import com.xinzhidi.web.dataset.domain.po.DataDevicePo;
import com.xinzhidi.web.dataset.domain.po.DataSetPo;
import com.xinzhidi.web.dataset.mapper.DataSetMapper;
import com.xinzhidi.web.dataset.vo.DataDeviceVo;
import com.xinzhidi.web.dataset.vo.DataSetInfoVo;
import com.xinzhidi.web.dataset.vo.DataSetListVo;
import com.xinzhidi.web.equipment.domain.Equipment;
import com.xinzhidi.web.equipment.domain.vo.EquipmentInfoVo;
import com.xinzhidi.web.equipment.service.EquipmentService;
import com.xinzhidi.web.house.domain.Household;
import com.xinzhidi.web.house.service.IHouseholdService;
import com.xinzhidi.web.netty.entity.AgreementCommand;
import com.xinzhidi.web.netty.entity.DeviceInstruction;
import com.xinzhidi.web.netty.entity.po.AgreementCommandPo;
import com.xinzhidi.web.netty.entity.po.AgreementHeatAvgPo;
import com.xinzhidi.web.netty.service.AgreementCommandService;
import com.xinzhidi.web.netty.service.AgreementHeatService;
import com.xinzhidi.web.netty.service.AgreementHouseholdValveService;
import com.xinzhidi.web.netty.service.DeviceInstructionService;
import com.xinzhidi.web.netty.utils.FormatData;
import com.xinzhidi.web.netty.utils.HexadecimalUtil;
import com.xinzhidi.web.plot.domain.entity.CPlot;
import com.xinzhidi.web.plot.service.ICPlotService;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * 初始设置Service业务层处理
 *
 * @author cx
 * @date 2023-06-27
 */
@Service
public class DataSetService extends ServiceImpl<DataSetMapper, DataSet> {

    @Autowired
    private DataSetMapper dataSetMapper;

    @Autowired
    private EquipmentService equipmentService;

    @Autowired
    private AgreementHeatService agreementHeatService;

    @Autowired
    private AgreementCommandService agreementCommandService;

    @Autowired
    private DeviceInstructionService deviceInstructionService;

    @Autowired
    private DataSetService dataSetService;

    @Autowired
    private ICPlotService plotService;

    @Autowired
    private ICBuildService buildService;

    @Autowired
    private IBuildUnitService unitService;

    @Autowired
    private IHouseholdService householdService;

    @Autowired
    private ControlSetService controlSetService;

    @Autowired
    private AgreementHouseholdValveService agreementHouseholdValveService;

    @Autowired
    private AlarmSettingMapper settingMapper;

    /**
     * 查询初始设置
     *
     * @param id 初始设置主键
     * @return 初始设置
     */
    public DataSetInfoVo selectDataSetById(Long id) {
        DataSet dataSet = dataSetMapper.selectById(id);
        DataSetInfoVo infoVo = new DataSetInfoVo();
        BeanUtils.copyProperties(dataSet, infoVo);
        return infoVo;
    }

    /**
     * 查询初始设置列表
     *
     * @param dto 查询初始设置dto
     * @return 初始设置
     */
    public TableDataInfo<DataSetListVo> selectDataSetList(DataSetListDto dto) {
        LambdaQueryWrapper<DataSet> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(dto.getPlotId() != null, DataSet::getPlotId, dto.getPlotId());
        wrapper.eq(dto.getBuildId() != null, DataSet::getBuildId, dto.getBuildId());
        wrapper.eq(dto.getUnitId() != null, DataSet::getUnitId, dto.getUnitId());
        wrapper.eq(StringUtils.isNotEmpty(dto.getType()), DataSet::getType, dto.getType());
        wrapper.orderByDesc(DataSet::getId);
        return TableDataInfo.getDataTable(dataSetMapper.selectList(wrapper), (domain -> {
            DataSetListVo vo = new DataSetListVo();
            BeanUtils.copyProperties(domain, vo);
            return vo;
        }));
    }

    /**
     * 新增初始设置
     *
     * @param dto 新增初始设置dto
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    public int insertDataSet(DataSetDto dto) {
        return dataSetService.saveDataSet(dto);
    }

    public int saveDataSet(DataSetDto dto) {
        LoginUser loginUser = SecurityUtils.getLoginUser();

        DataSet dataSet = new DataSet();
        BeanUtils.copyProperties(dto, dataSet);
        dataSet.setCreateBy(loginUser.getUserId().toString());
        dataSet.setCreateTime(DateUtils.getNowDate());
        int insert = dataSetMapper.insert(dataSet);
        //保存指令
//        saveInstruct(dto);
        //下发指令
        sendMessage("1", dto, AgreementConstants.InstructBusinessType.DATA_SET);

        this.updateRelevanceData("1", dto.getType(), dto.getPlotId(), dto.getBuildId(), dto.getUnitId(), dto.getHouseholdId(), dto.getRegulationMethod(), dto.getControlStrategy(), dto.getPriority());
        return insert;
    }

    /**
     * 修改初始设置
     *
     * @param dto 修改初始设置dto
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    public int updateDataSet(DataSetDto dto) {
        LoginUser loginUser = SecurityUtils.getLoginUser();

        DataSet dataSet = new DataSet();
        BeanUtils.copyProperties(dto, dataSet);
        dataSet.setUpdateBy(loginUser.getUserId().toString());
        dataSet.setUpdateTime(DateUtils.getNowDate());
        int update = dataSetMapper.updateById(dataSet);
        //保存指令
//        saveInstruct(dto);
        //下发指令
        sendMessage("1", dto, AgreementConstants.InstructBusinessType.DATA_SET);

        this.updateRelevanceData("1", dto.getType(), dto.getPlotId(), dto.getBuildId(), dto.getUnitId(), dto.getHouseholdId(), dto.getRegulationMethod(), dto.getControlStrategy(), dto.getPriority());
        return update;
    }

    /**
     * 修改相关初始设置  如设置了某个小区，则同时设置小区下的楼栋、单元、住户表的调控方法及调控策略
     *
     * @param setType          设置类型（1：初始设置  2：平衡调控）
     * @param type             类型（类型（0：小区  2：楼栋  2：单元））
     * @param plotId           小区id
     * @param buildId          楼栋id
     * @param unitId           单元id
     * @param householdId      住户id
     * @param regulationMethod 调控方法（1：手动调控  2：自动调控）
     * @param controlStrategy  调控策略:1 回水温度 2 室内温度 3 供回水平均温度 4 流量 5 热量
     * @param priority         优先级（1：楼栋  2：单元）
     * @return 结果
     */
    public void updateRelevanceData(String setType, String type, Long plotId, Long buildId, Long unitId, Long householdId, String regulationMethod, String controlStrategy, Integer priority) {
        if (type.equals(AgreementConstants.DataType.PLOT)) {
            this.updatePlotRelevanceData(setType, type, plotId, regulationMethod, controlStrategy);
        } else if (type.equals(AgreementConstants.DataType.BUILD)) {
            this.updateBuildRelevanceData(setType, type, buildId, regulationMethod, controlStrategy, priority);
        } else if (type.equals(AgreementConstants.DataType.BUILD_UNIT)) {
            this.updateUnitRelevanceData(setType, type, unitId, regulationMethod, controlStrategy);
        } else if (type.equals(AgreementConstants.DataType.HOUSEHOLD)) {
            this.updateHouseholdRelevanceData(setType, type, householdId, regulationMethod, controlStrategy);
        }
    }

    /**
     * 修改住户相关初始设置
     *
     * @param setType          设置类型（1：初始设置  2：平衡调控）
     * @param type             类型（类型（0：小区  1：楼栋  2：单元 3：住户））
     * @param householdId      住户id
     * @param regulationMethod 调控方法（1：手动调控  2：自动调控）
     * @param controlStrategy  调控策略:1 回水温度 2 室内温度 3 供回水平均温度 4 流量 5 热量
     */
    private void updateHouseholdRelevanceData(String setType, String type, Long householdId, String regulationMethod, String controlStrategy) {
        Household cHousehold = householdService.getById(householdId);
        cHousehold.setRegulationMethod(regulationMethod);
        cHousehold.setControlStrategy(controlStrategy);
        cHousehold.setSetLevel(type);
        cHousehold.setSetType(setType);
        householdService.updateById(cHousehold);
    }

    /**
     * 修改单元相关初始设置
     *
     * @param setType          设置类型（1：初始设置  2：平衡调控）
     * @param type             类型（类型（0：小区  2：楼栋  2：单元））
     * @param unitId           单元id
     * @param regulationMethod 调控方法（1：手动调控  2：自动调控）
     * @param controlStrategy  调控策略:1 回水温度 2 室内温度 3 供回水平均温度 4 流量 5 热量
     */
    private void updateUnitRelevanceData(String setType, String type, Long unitId, String regulationMethod, String controlStrategy) {
        BuildUnit cBuildUnit = unitService.getById(unitId);
        cBuildUnit.setRegulationMethod(regulationMethod);
        cBuildUnit.setControlStrategy(controlStrategy);
        cBuildUnit.setSetLevel(type);
        cBuildUnit.setSetType(setType);
        unitService.updateById(cBuildUnit);
        //修改住户表
        UpdateWrapper<Household> householdWrapper = new UpdateWrapper<>();
        householdWrapper.set("set_type", setType)
                .set("set_level", type)
                .set("regulation_method", regulationMethod)
                .set("control_strategy", controlStrategy)
                .eq("unit", unitId);
        householdService.update(householdWrapper);
    }

    /**
     * 修改楼栋相关初始设置
     *
     * @param setType          设置类型（1：初始设置  2：平衡调控）
     * @param type             类型（类型（0：小区  2：楼栋  2：单元））
     * @param buildId          楼栋id
     * @param regulationMethod 调控方法（1：手动调控  2：自动调控）
     * @param controlStrategy  调控策略:1 回水温度 2 室内温度 3 供回水平均温度 4 流量 5 热量
     * @param priority         优先级（1：楼栋  2：单元）
     */
    private void updateBuildRelevanceData(String setType, String type, Long buildId, String regulationMethod, String controlStrategy, Integer priority) {
        CBuild cBuild = buildService.getById(buildId);
        cBuild.setSetLevel(type);
        cBuild.setRegulationMethod(regulationMethod);
        cBuild.setControlStrategy(controlStrategy);
        cBuild.setSetType(setType);
        buildService.updateById(cBuild);

        if (priority == null) {
            throw new ServiceException("优先级不能为空!");
        }

        //如果优先级为楼栋，则修改该楼栋下的单元及住户的调控方法及调控策略
        if (priority.equals(AgreementConstants.PriorityType.BUILD)) {
            //修改该楼栋下的所有单元
            UpdateWrapper<BuildUnit> updateWrapper = new UpdateWrapper<>();
            updateWrapper.set("set_type", setType)
                    .set("set_level", type)
                    .set("regulation_method", regulationMethod)
                    .set("control_strategy", controlStrategy)
                    .eq("build_id", buildId);
            unitService.update(updateWrapper);

            //修改该楼栋下的所有住户
            UpdateWrapper<Household> householdWrapper = new UpdateWrapper<>();
            householdWrapper.set("set_type", setType)
                    .set("set_level", type)
                    .set("regulation_method", regulationMethod)
                    .set("control_strategy", controlStrategy)
                    .eq("build_id", buildId);
            householdService.update(householdWrapper);
        }
    }

    /**
     * 修改小区相关初始设置
     *
     * @param setType          设置类型（1：初始设置  2：平衡调控）
     * @param type             类型（类型（0：小区  2：楼栋  2：单元））
     * @param plotId           小区id
     * @param regulationMethod 调控方法（1：手动调控  2：自动调控）
     * @param controlStrategy  调控策略:1 回水温度 2 室内温度 3 供回水平均温度 4 流量 5 热量
     * @return 结果
     */
    private void updatePlotRelevanceData(String setType, String type, Long plotId, String regulationMethod, String controlStrategy) {

        CPlot cPlot = plotService.getById(plotId);
        cPlot.setSetLevel(type);
        cPlot.setRegulationMethod(regulationMethod);
        cPlot.setControlStrategy(controlStrategy);
        cPlot.setSetType(setType);
        plotService.updateById(cPlot);

        //修改该小区下的所有楼栋
        UpdateWrapper<CBuild> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("set_type", setType)
                .set("set_level", type)
                .set("regulation_method", regulationMethod)
                .set("control_strategy", controlStrategy)
                .eq("plot_id", plotId);
        buildService.update(updateWrapper);

        //修改该小区下的所有单元
        UpdateWrapper<BuildUnit> unitWrapper = new UpdateWrapper<>();
        unitWrapper.set("set_type", setType)
                .set("set_level", type)
                .set("regulation_method", regulationMethod)
                .set("control_strategy", controlStrategy)
                .eq("lot_id", plotId);
        unitService.update(unitWrapper);

        //修改该小区下的所有住户
        UpdateWrapper<Household> householdWrapper = new UpdateWrapper<>();
        householdWrapper.set("set_type", setType)
                .set("set_level", type)
                .set("regulation_method", regulationMethod)
                .set("control_strategy", controlStrategy)
                .eq("plot_id", plotId);
        householdService.update(householdWrapper);
    }


    /**
     * 下发指令
     *
     * @param dto          修改初始设置dto
     * @param businessType 业务类型
     * @return 结果
     */
    public void sendMessage(String setType, DataSetDto dto, Integer businessType) {
        String valveOpenStr;
        List<EquipmentInfoVo> equipmentInfoVos = Lists.newArrayList();
        //流程备注：
        // 1、根据初始类型获取需要下发指令的设备list  如：初始类型为小区，则获取小区下的所有楼栋、单元、住户设备list。初始类型为楼栋，则获取楼栋及楼栋下的所有单元、住户设备list，以此类推
        // 2、柱状该设备相关的阀门指令
        // 3、保存指令到指令发送表
        switch (dto.getType()) {
            case AgreementConstants.DataType.PLOT:
                //需要对小区下的所有楼栋、单元、住户下发指令
                List<Long> plotIds = Arrays.asList(dto.getPlotId());
                equipmentInfoVos = equipmentService.getEquipmentListByDim(setType, AgreementConstants.DataType.PLOT, plotIds);
                break;
            case AgreementConstants.DataType.BUILD:
                //楼栋---组装所有该楼栋下的单元阀门协议及户用阀门协议指令
                //获取楼栋设备list(楼栋下的单元阀门及户用阀门)
                List<Long> buildIds = Arrays.asList(dto.getBuildId());
                equipmentInfoVos = equipmentService.getEquipmentListByDim(setType, AgreementConstants.DataType.BUILD, buildIds);
                if (CollectionUtils.isEmpty(equipmentInfoVos)) {
                    return;
                }
                break;
            case AgreementConstants.DataType.BUILD_UNIT:
                //单元---组装所有该单元下的的单元阀门协议及户用阀门协议指令
                //获取单元设备list(单元下的单元阀门及户用阀门)
                List<Long> buildUnitIds = Arrays.asList(dto.getUnitId());
                equipmentInfoVos = equipmentService.getEquipmentListByDim(setType, AgreementConstants.DataType.BUILD_UNIT, buildUnitIds);
                break;
            case AgreementConstants.DataType.HOUSEHOLD:
                //住户---组装该住户下的户用阀门协议指令
                //获取住户设备list(住户下的户用阀门)
                List<Long> householdIds = Arrays.asList(dto.getHouseholdId());
                equipmentInfoVos = equipmentService.getEquipmentListByDim(setType, AgreementConstants.DataType.HOUSEHOLD, householdIds);
                break;
            default:
                break;
        }
        //组装指令
        AgreementCommandPo commandPo;
        AgreementCommand command;
        DeviceInstruction addDto;
        List<DeviceInstruction> list = Lists.newArrayList();
        String protocolType = "";
        List<AlarmSettingListVo> settingList = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(equipmentInfoVos)) {
            AlarmSettingListDto settingListDto = new AlarmSettingListDto();
            settingListDto.setAlarmType("1");
            settingListDto.setPageSize(100);
            settingList = settingMapper.getSettingList(settingListDto);
        }

        for (EquipmentInfoVo equipmentInfoVo : equipmentInfoVos) {
            protocolType = equipmentInfoVo.getProtocolType();
            //如果设备对应户用阀门协议
            if (protocolType.equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)
                    || protocolType.equals(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER)) {
                valveOpenStr = String.valueOf(dto.getHouseholdValveOpen());
            } else if (protocolType.equals(AgreementConstants.AgreementType.UNIT_VALVE)) {
                //如果设备对应单元阀门协议
                valveOpenStr = String.valueOf(dto.getUnitValveOpen());
            } else {
                continue;
            }

            if (protocolType.equals(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER)) {
                //如果是海龙马调节阀协议协议或者单元阀门协议，需要将开度值转为对应的阀门开度值
                if (Integer.parseInt(valveOpenStr) == 100) {
                    valveOpenStr = "0100";
                } else {
                    if (Integer.parseInt(valveOpenStr) < 10) {
                        valveOpenStr = "000" + valveOpenStr;
                    } else {
                        valveOpenStr = "00" + valveOpenStr;
                    }
                }
                valveOpenStr = "01" + HexadecimalUtil.convertToHighLow(valveOpenStr, 1);
            } else {
                //因为开度值的系数为0.1，即读的时候要除以10，那么设置的时候就要乘以10
                valveOpenStr = new BigDecimal(valveOpenStr).multiply(new BigDecimal(10)).toString();
                if (protocolType.equals(AgreementConstants.AgreementType.UNIT_VALVE)) {
                    valveOpenStr = HexadecimalUtil.convertToHighLow(FormatData.tenToHexadecimal(Integer.valueOf(valveOpenStr), 2), 1);
                } else {
                    //将valveOpen转为高低位的十六进制
                    valveOpenStr = FormatData.tenToHexadecimal(Integer.valueOf(valveOpenStr), 2);
                }

            }

            addDto = new DeviceInstruction();
            commandPo = new AgreementCommandPo();
            if (equipmentInfoVo.getProtocolType().equals(AgreementConstants.AgreementType.UNIT_VALVE)) {
                commandPo.setProtocolType(AgreementConstants.AgreementType.UNIT_VALVE);
            } else {
                commandPo.setProtocolType(equipmentInfoVo.getProtocolType());
            }
            commandPo.setType(AgreementConstants.InstructionType.SEND);

            commandPo.setInstructType("3");
            command = agreementCommandService.getProtocolInstruction(commandPo);
            addDto.setDtuId(equipmentInfoVo.getDtuId());
            addDto.setProtocolType(equipmentInfoVo.getProtocolType());
            addDto.setProtocolName(command.getProtocolName());
            addDto.setInstructType(command.getInstructType());
            addDto.setInstructName(command.getInstructName());
            addDto.setPlotId(equipmentInfoVo.getLotId());
            addDto.setBuildId(equipmentInfoVo.getBuildId());
            addDto.setUnitId(equipmentInfoVo.getUnitId());
            addDto.setHouseholdId(equipmentInfoVo.getHouseholdId());
            addDto.setAddress(equipmentInfoVo.getAddress());
            String instructionSet = command.getPrefix() + command.getStartChar() + command.getDeviceType() + equipmentInfoVo.getAddress() +
                    command.getControlCode() + command.getLengthField() + command.getDataFieldInstruct()
                    + (StringUtils.isBlank(command.getDataMark()) ? "" : command.getDataMark()) + valveOpenStr;
            //获取校验码
            String checkCode = HexadecimalUtil.getCheckCode(instructionSet);
            instructionSet += checkCode + command.getEndMark();
            addDto.setInstructionSet(instructionSet);

//            if (settingList.size() > 0) {
//                //过滤出settingList中告警类型等于protocolType的数据
//                AlarmSettingListVo vo = settingList.stream().filter(alarmSettingListVo -> alarmSettingListVo.getAlarmType().equals(equipmentInfoVo.getProtocolType())).findFirst().orElse(null);
//                if (vo != null) {
//                    addDto.setExceedTimes(vo.getExceedTimes());
//                }
//            }
            list.add(addDto);
        }
        if (list.size() > 0) {
            list.forEach(deviceInstruction -> deviceInstruction.setBusinessType(businessType));
            //保存指令
            list = list.stream()
                    .collect(Collectors.toMap(
                            obj -> Arrays.asList(obj.getDtuId(), obj.getAddress(), obj.getInstructType()), // 使用多个字段组合作为键
                            obj -> obj,
                            (existing, replacement) -> existing // 如果存在重复，保留第一个
                    ))
                    .values()
                    .stream()
                    .collect(Collectors.toList());
            deviceInstructionService.saveBatch(list);
        }
        //修改设备表的调节方式
        List<Long> equipmentIds = equipmentInfoVos.stream().map(EquipmentInfoVo::getId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(equipmentIds)) {
            UpdateWrapper updateWrapper = new UpdateWrapper();
            updateWrapper.set("regulation_method", dto.getRegulationMethod());
            updateWrapper.in("id", equipmentIds);
            equipmentService.update(updateWrapper);
        }
    }

    /**
     * 保存指令
     *
     * @param dto 新增初始设置dto
     */
    public void saveInstruct(DataSetDto dto) {
        //1：查询有哪些设备是属于该楼栋/单元的（包括楼栋/单元设备设备的单元阀门及户用阀门）
        //2：分别计算单元阀门及户用阀门开度的平均值
        //3：调用客户提供的算法重新计算阀门开度值
        //4：组装指令
        //5：保存指令

        List<Long> unitIds = Lists.newArrayList();
        List<EquipmentInfoVo> buildEquipmentList = Lists.newArrayList();
        switch (dto.getType()) {
            case AgreementConstants.DataType.BUILD:
                //楼栋---组装所有该楼栋下的单元阀门协议及户用阀门协议指令
                //获取楼栋设备list(楼栋下的单元阀门及户用阀门)
                List<Long> buildIds = new ArrayList<>();
                buildIds.add(dto.getBuildId());
                buildEquipmentList = equipmentService.getBuildEquipmentList(buildIds);
                if (CollectionUtils.isEmpty(buildEquipmentList)) {
                    return;
                }
                //获取单元下户用阀门设备list
                unitIds = buildEquipmentList.stream().map(EquipmentInfoVo::getUnitId).collect(Collectors.toList());
                break;
            case AgreementConstants.DataType.BUILD_UNIT:
                //单元---组装所有该单元下的的单元阀门协议及户用阀门协议指令
                //获取单元设备list(单元下的单元阀门及户用阀门)
                unitIds.add(dto.getUnitId());
                break;
            default:
                break;
        }
        List<EquipmentInfoVo> HouseholdEquipmentList = equipmentService.getHouseholdEquipmentList(unitIds);
        //组装单元阀门协议指令（下发阀门开度）
        List<EquipmentInfoVo> totalEquipmentList = new ArrayList<>();
        totalEquipmentList.addAll(buildEquipmentList);
        totalEquipmentList.addAll(HouseholdEquipmentList);
        //过滤掉没有modelId的设备
        totalEquipmentList = totalEquipmentList.stream().filter(equipmentInfoVo -> equipmentInfoVo.getModelId() != null).collect(Collectors.toList());
//        createBuildMessage(totalEquipmentList, dto);
    }

    /**
     * 组装指令
     *
     * @param dto 新增初始设置dto
     */
    public void createBuildMessage(List<EquipmentInfoVo> totalEquipmentList, DataSetDto dto, List<Integer> modelIds, Map<String, Object> map, DeviceInstruction oldInstruction) {

        //如果没设置步进值，则默认步进值为5
        if (dto.getStepValue() == null) {
            dto.setStepValue(5);
        }

        DeviceInstruction deviceInstruction = null;
        List<DeviceInstruction> deviceInstructionList = new ArrayList<>();
        for (EquipmentInfoVo equipmentInfoVo : totalEquipmentList) {
            switch (dto.getControlStrategy()) {
                case AgreementConstants.ControlTactics.RETURN_WATER_TEMPERATURE:
                    //回水温度
                    deviceInstruction = createReturnWaterTemperature(equipmentInfoVo, dto, modelIds, map);
                    break;
                case AgreementConstants.ControlTactics.ROOM_TEMPERATURE:
                    //室温
//                    deviceInstruction = createRoomTemperature(equipmentInfoVo, dto, modelIds, map);
                    break;
                case AgreementConstants.ControlTactics.AVERAGE_AND_RETURN_WATER:
                    //供回水平均温度
                    deviceInstruction = createAverageAndReturnWater(equipmentInfoVo, dto, modelIds, map);
                    break;
                case AgreementConstants.ControlTactics.FLOW_RATE:
                    //流量
                    deviceInstruction = createVelocityFlow(equipmentInfoVo, dto, modelIds, map);
                    break;
                case AgreementConstants.ControlTactics.HEAT:
                    //热量
                    deviceInstruction = createHeat(equipmentInfoVo, dto, modelIds, map);
                    break;
                default:
                    break;
            }
            deviceInstruction.setBusinessType(oldInstruction.getBusinessType());
            if (deviceInstruction != null) {
                deviceInstructionList.add(deviceInstruction);
            }
        }
        if (CollectionUtils.isNotEmpty(deviceInstructionList)) {
            deviceInstructionService.saveBatch(deviceInstructionList);
        }

    }

    /**
     * 组装回水温度指令
     *
     * @param equipmentInfoVo 设备
     */
    public DeviceInstruction createReturnWaterTemperature(EquipmentInfoVo equipmentInfoVo, DataSetDto dto, List<Integer> modelIdList, Map<String, Object> map) {
        BigDecimal value;
        String valveOpenStr = "";
        AgreementCommandPo commandPo = new AgreementCommandPo();

        AgreementHeatAvgPo po = new AgreementHeatAvgPo();
        po.setModelIds(modelIdList);
        po.setFiledName("\'$.returnWaterTemperature\'");
        Household household;
        BuildUnit unit;

        //接收到的回水温度
        BigDecimal returnWaterTemperature = new BigDecimal(map.get("returnWaterTemperature").toString());

        BigDecimal valveOpen;
        Integer open;

        //最大阀门开度
        BigDecimal maxValveOpen = new BigDecimal("100");
        //最小阀门开度（即关断值）
        BigDecimal minValveOpen = new BigDecimal("30");

        BigDecimal stepValue = new BigDecimal(String.valueOf(dto.getStepValue()));
//        Integer valveOpen = 0;
        DeviceInstruction instruction = new DeviceInstruction();
        instruction.setDtuId(equipmentInfoVo.getDtuId());
        Equipment e = new Equipment();
        //如果设备是住户，则获取和该住户同模型的回水温度平均值
        if (equipmentInfoVo.getProtocolType().equals(AgreementConstants.AgreementType.UNIT_HEAT_METER)) {
            po.setProtocolType(AgreementConstants.AgreementType.UNIT_HEAT_METER);
            commandPo.setProtocolType(AgreementConstants.AgreementType.UNIT_VALVE);
            instruction.setProtocolType(AgreementConstants.AgreementType.UNIT_HEAT_METER);
            instruction.setProtocolName(AgreementConstants.AgreementTypeName.UNIT_VALVE);
            unit = unitService.getById(equipmentInfoVo.getUnitId());
            open = unit.getValveOpen() == null ? 0 : unit.getValveOpen();
            valveOpen = new BigDecimal(open);

            e = equipmentService.getOne(Wrappers.<Equipment>lambdaQuery()
                    .eq(Equipment::getUnitId, equipmentInfoVo.getUnitId())
                    .eq(Equipment::getProtocolType, AgreementConstants.AgreementType.UNIT_VALVE));

            instruction.setProtocolType(AgreementConstants.AgreementType.UNIT_VALVE);
            instruction.setProtocolName(AgreementConstants.AgreementTypeName.UNIT_VALVE);

        } else {
            //判断住户是户用阀门协议还是海龙马阀门协议
            List<Equipment> equipmentList = equipmentService.list(Wrappers.<Equipment>lambdaQuery()
                    .eq(Equipment::getHouseholdId, equipmentInfoVo.getHouseholdId())
                    .and((w) -> {
                        w.eq(Equipment::getProtocolType, AgreementConstants.AgreementType.HOUSEHOLD_VALVE)
                                .or().eq(Equipment::getProtocolType, AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                    }));

            if (equipmentList.isEmpty()) {
                return null;
            }
            e = equipmentList.get(0);
            po.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_HEAT_METER);
            if (e.getProtocolType().equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)) {
                commandPo.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_VALVE);
                instruction.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_VALVE);
                instruction.setProtocolName(AgreementConstants.AgreementTypeName.HOUSEHOLD_VALVE);
            } else {
                commandPo.setProtocolType(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                instruction.setProtocolType(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                instruction.setProtocolName(AgreementConstants.AgreementTypeName.SEAHORSE_VALVE_METER);
            }

            household = householdService.getById(equipmentInfoVo.getHouseholdId());
            open = household.getValveOpen() == null ? 0 : household.getValveOpen();
            valveOpen = new BigDecimal(open);
        }

        //获取回水温度平均值
        value = agreementHeatService.getAvgValue(po);
        //获取当前设备开度
        if (returnWaterTemperature.compareTo(value) != 0) {
            //如果当前回水温度小于最小值,则需要开大开度
            if (returnWaterTemperature.compareTo(value) < 0) {
                if (valveOpen.add(stepValue).compareTo(maxValveOpen) > -1) {
                    valveOpen = maxValveOpen;
                } else {
                    valveOpen = valveOpen.add(stepValue);
                }
            }
            //如果当前回水温度大于最大值,则需要关小开度
            if (returnWaterTemperature.compareTo(value) > 0) {
                if (valveOpen.subtract(stepValue).compareTo(minValveOpen) < 1) {
                    valveOpen = minValveOpen;
                } else {
                    valveOpen = valveOpen.subtract(stepValue);
                }
            }

        }
        //因为开度值的系数为0.1，即读的时候要除以10，那么设置的时候就要乘以10
        if (e.getProtocolType().equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)) {
            //如果采用户用阀门协议
            valveOpenStr = valveOpen.multiply(new BigDecimal(10)).toString();
            valveOpenStr = FormatData.tenToHexadecimal(valveOpen.intValue(), 2);
        } else if (e.getProtocolType().equals(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER)) {
            //如果采用海龙马阀门协议
            if (valveOpen.compareTo(new BigDecimal(100)) == 0) {
                valveOpenStr = "0001";
            } else {
                String v = String.valueOf(valveOpen);
                if (v.length() == 1) {
                    valveOpenStr = "0" + v + "00";
                } else if (v.length() == 2) {
                    valveOpenStr = v + "00";
                }
            }
            valveOpenStr = "01" + valveOpenStr;
        }

        commandPo.setType(AgreementConstants.InstructionType.SEND);


        commandPo.setInstructType("3");
        AgreementCommand command = agreementCommandService.getProtocolInstruction(commandPo);
        instruction.setInstructType(command.getInstructType());
        instruction.setInstructName(command.getInstructName());
        instruction.setPlotId(e.getLotId());
        instruction.setBuildId(e.getBuildId());
        instruction.setUnitId(e.getUnitId());
        instruction.setHouseholdId(e.getHouseholdId());
        instruction.setAddress(e.getAddress());
        String instructionSet = command.getPrefix() + command.getStartChar() + command.getDeviceType() + e.getAddress() +
                command.getControlCode() + command.getLengthField() + command.getDataFieldInstruct()
                + command.getDataMark() + valveOpenStr;
        //获取校验码
        String checkCode = HexadecimalUtil.getCheckCode(instructionSet);
        instructionSet += checkCode + command.getEndMark();
        instruction.setInstructionSet(instructionSet);
        return instruction;
    }

    /**
     * 组装流量指令
     *
     * @param equipmentInfoVo 设备
     */
    private DeviceInstruction createVelocityFlow(EquipmentInfoVo equipmentInfoVo, DataSetDto dto, List<Integer> modelIds, Map<String, Object> map) {
        BigDecimal value = BigDecimal.ZERO;
        String valveOpenStr = "";
        AgreementCommandPo commandPo = new AgreementCommandPo();
        AgreementHeatAvgPo po = new AgreementHeatAvgPo();
        po.setModelIds(modelIds);
        po.setFiledName("\'$.velocityFlow\'");
        Household household;
        BuildUnit unit;

        //接收到的流量
        BigDecimal velocityFlow = new BigDecimal(map.get("velocityFlow").toString());

        BigDecimal valveOpen;

        Integer open;

        //最大阀门开度
        BigDecimal maxValveOpen = new BigDecimal("100");
        //最小阀门开度（即关断值）
        BigDecimal minValveOpen = new BigDecimal("30");

        BigDecimal stepValue = new BigDecimal(String.valueOf(dto.getStepValue()));

//        Integer valveOpen = 0;
        DeviceInstruction instruction = new DeviceInstruction();
        instruction.setDtuId(equipmentInfoVo.getDtuId());
        //如果设备是单元，则获取和该单元同模型的流量平均值
        //如果设备是住户，则获取和该住户同模型的流量平均值
        Equipment e = new Equipment();
        if (equipmentInfoVo.getProtocolType().equals(AgreementConstants.AgreementType.UNIT_HEAT_METER)) {
            po.setProtocolType(AgreementConstants.AgreementType.UNIT_HEAT_METER);
            commandPo.setProtocolType(AgreementConstants.AgreementType.UNIT_VALVE);
            instruction.setProtocolType(AgreementConstants.AgreementType.UNIT_HEAT_METER);
            instruction.setProtocolName(AgreementConstants.AgreementTypeName.UNIT_VALVE);
            unit = unitService.getById(equipmentInfoVo.getUnitId());
            open = unit.getValveOpen() == null ? 0 : unit.getValveOpen();
            valveOpen = new BigDecimal(open);

            e = equipmentService.getOne(Wrappers.<Equipment>lambdaQuery()
                    .eq(Equipment::getUnitId, equipmentInfoVo.getUnitId())
                    .eq(Equipment::getProtocolType, AgreementConstants.AgreementType.UNIT_VALVE));

            instruction.setProtocolType(AgreementConstants.AgreementType.UNIT_VALVE);
            instruction.setProtocolName(AgreementConstants.AgreementTypeName.UNIT_VALVE);

        } else {
            //判断住户是户用阀门协议还是海龙马阀门协议
            List<Equipment> equipmentList = equipmentService.list(Wrappers.<Equipment>lambdaQuery()
                    .eq(Equipment::getHouseholdId, equipmentInfoVo.getHouseholdId())
                    .and((w) -> {
                        w.eq(Equipment::getProtocolType, AgreementConstants.AgreementType.HOUSEHOLD_VALVE)
                                .or().eq(Equipment::getProtocolType, AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                    }));

            if (equipmentList.isEmpty()) {
                return null;
            }
            e = equipmentList.get(0);
            po.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_HEAT_METER);
            if (e.getProtocolType().equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)) {
                commandPo.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_VALVE);
                instruction.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_VALVE);
                instruction.setProtocolName(AgreementConstants.AgreementTypeName.HOUSEHOLD_VALVE);
            } else {
                commandPo.setProtocolType(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                instruction.setProtocolType(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                instruction.setProtocolName(AgreementConstants.AgreementTypeName.SEAHORSE_VALVE_METER);
            }

            household = householdService.getById(equipmentInfoVo.getHouseholdId());
            open = household.getValveOpen() == null ? 0 : household.getValveOpen();
            valveOpen = new BigDecimal(open);
        }

        //获取流量平均值
        value = agreementHeatService.getAvgValue(po);
        if (velocityFlow.compareTo(value) != 0) {
            //如果当前回水温度小于最小值,则需要开大开度
            if (velocityFlow.compareTo(value) < 0) {
                if (valveOpen.add(stepValue).compareTo(maxValveOpen) > -1) {
                    valveOpen = maxValveOpen;
                } else {
                    valveOpen = valveOpen.add(stepValue);
                }
            }
            //如果当前回水温度大于最大值,则需要关小开度
            if (velocityFlow.compareTo(value) > 0) {
                if (valveOpen.subtract(stepValue).compareTo(minValveOpen) < 1) {
                    valveOpen = minValveOpen;
                } else {
                    valveOpen = valveOpen.subtract(stepValue);
                }
            }

        }
        //因为开度值的系数为0.1，即读的时候要除以10，那么设置的时候就要乘以10
        if (e.getProtocolType().equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)) {
            //如果采用户用阀门协议
            valveOpenStr = valveOpen.multiply(new BigDecimal(10)).toString();
            valveOpenStr = FormatData.tenToHexadecimal(Integer.parseInt(valveOpenStr), 2);
        } else if (e.getProtocolType().equals(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER)) {
            //如果采用海龙马阀门协议
            if (valveOpen.compareTo(new BigDecimal(100)) == 0) {
                valveOpenStr = "0001";
            } else {
                String v = String.valueOf(valveOpen);
                if (v.length() == 1) {
                    valveOpenStr = "0" + v + "00";
                } else if (v.length() == 2) {
                    valveOpenStr = v + "00";
                }
            }
            valveOpenStr = "01" + valveOpenStr;
        }
        //将valveOpen转为高低位的十六进制
        commandPo.setType(AgreementConstants.InstructionType.SEND);
//        valveOpenStr = HexadecimalUtil.hexConvertToHighLow(FormatData.tenToHexadecimal(valveOpen.intValue(), 2));

        commandPo.setInstructType("3");
        AgreementCommand command = agreementCommandService.getProtocolInstruction(commandPo);
        instruction.setInstructType(command.getInstructType());
        instruction.setInstructName(command.getInstructName());
        instruction.setPlotId(e.getLotId());
        instruction.setBuildId(e.getBuildId());
        instruction.setUnitId(e.getUnitId());
        instruction.setHouseholdId(e.getHouseholdId());
        instruction.setAddress(e.getAddress());
        String instructionSet = command.getPrefix() + command.getStartChar() + command.getDeviceType() + e.getAddress() +
                command.getControlCode() + command.getLengthField() + command.getDataFieldInstruct()
                + command.getDataMark() + valveOpenStr;
        //获取校验码
        String checkCode = HexadecimalUtil.getCheckCode(instructionSet);
        instructionSet += checkCode + command.getEndMark();
        instruction.setInstructionSet(instructionSet);
        return instruction;
    }


    /**
     * 组装热量指令
     *
     * @param equipmentInfoVo 设备
     */
    private DeviceInstruction createHeat(EquipmentInfoVo equipmentInfoVo, DataSetDto dto, List<Integer> modelIds, Map<String, Object> map) {
        BigDecimal value;
        String valveOpenStr = "";
        AgreementCommandPo commandPo = new AgreementCommandPo();
        AgreementHeatAvgPo po = new AgreementHeatAvgPo();
        po.setModelIds(modelIds);
        po.setFiledName("\'$.currentHeatOrCold\'");
        Household household;
        BuildUnit unit;

        //接收到的热量
        BigDecimal currentHeatOrCold = new BigDecimal(map.get("currentHeatOrCold").toString());

        BigDecimal valveOpen;
        Integer open;
        //最大阀门开度
        BigDecimal maxValveOpen = new BigDecimal("100");
        //最小阀门开度（即关断值）
        BigDecimal minValveOpen = new BigDecimal("30");

        BigDecimal stepValue = new BigDecimal(String.valueOf(dto.getStepValue()));

//        Integer valveOpen = 0;
        DeviceInstruction instruction = new DeviceInstruction();
        instruction.setDtuId(equipmentInfoVo.getDtuId());
        //如果设备是单元，则获取和该单元同模型的热量平均值
        //如果设备是住户，则获取和该住户同模型的热量平均值
        Equipment e = new Equipment();
        if (equipmentInfoVo.getProtocolType().equals(AgreementConstants.AgreementType.UNIT_HEAT_METER)) {
            po.setProtocolType(AgreementConstants.AgreementType.UNIT_HEAT_METER);
            commandPo.setProtocolType(AgreementConstants.AgreementType.UNIT_VALVE);
            instruction.setProtocolType(AgreementConstants.AgreementType.UNIT_HEAT_METER);
            instruction.setProtocolName(AgreementConstants.AgreementTypeName.UNIT_VALVE);
            unit = unitService.getById(equipmentInfoVo.getUnitId());
            open = unit.getValveOpen() == null ? 0 : unit.getValveOpen();
            valveOpen = new BigDecimal(open);

            e = equipmentService.getOne(Wrappers.<Equipment>lambdaQuery()
                    .eq(Equipment::getUnitId, equipmentInfoVo.getUnitId())
                    .eq(Equipment::getProtocolType, AgreementConstants.AgreementType.UNIT_VALVE));

            instruction.setProtocolType(AgreementConstants.AgreementType.UNIT_VALVE);
            instruction.setProtocolName(AgreementConstants.AgreementTypeName.UNIT_VALVE);

        } else {
            //判断住户是户用阀门协议还是海龙马阀门协议
            List<Equipment> equipmentList = equipmentService.list(Wrappers.<Equipment>lambdaQuery()
                    .eq(Equipment::getHouseholdId, equipmentInfoVo.getHouseholdId())
                    .and((w) -> {
                        w.eq(Equipment::getProtocolType, AgreementConstants.AgreementType.HOUSEHOLD_VALVE)
                                .or().eq(Equipment::getProtocolType, AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                    }));

            if (equipmentList.isEmpty()) {
                return null;
            }
            e = equipmentList.get(0);
            po.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_HEAT_METER);
            if (e.getProtocolType().equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)) {
                commandPo.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_VALVE);
                instruction.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_VALVE);
                instruction.setProtocolName(AgreementConstants.AgreementTypeName.HOUSEHOLD_VALVE);
            } else {
                commandPo.setProtocolType(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                instruction.setProtocolType(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                instruction.setProtocolName(AgreementConstants.AgreementTypeName.SEAHORSE_VALVE_METER);
            }

            household = householdService.getById(equipmentInfoVo.getHouseholdId());
            open = household.getValveOpen() == null ? 0 : household.getValveOpen();
            valveOpen = new BigDecimal(open);
        }

        //获取流量平均值
        value = agreementHeatService.getAvgValue(po);

        if (currentHeatOrCold.compareTo(value) != 0) {
            //如果当前回水温度小于最小值,则需要开大开度
            if (currentHeatOrCold.compareTo(value) < 0) {
                if (valveOpen.add(stepValue).compareTo(maxValveOpen) > -1) {
                    valveOpen = maxValveOpen;
                } else {
                    valveOpen = valveOpen.add(stepValue);
                }
            }
            //如果当前回水温度大于最大值,则需要关小开度
            if (currentHeatOrCold.compareTo(value) > 0) {
                if (valveOpen.subtract(stepValue).compareTo(minValveOpen) < 1) {
                    valveOpen = minValveOpen;
                } else {
                    valveOpen = valveOpen.subtract(stepValue);
                }
            }

        }

        //因为开度值的系数为0.1，即读的时候要除以10，那么设置的时候就要乘以10
        if (e.getProtocolType().equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)) {
            //如果采用户用阀门协议
            valveOpenStr = valveOpen.multiply(new BigDecimal(10)).toString();
            valveOpenStr = FormatData.tenToHexadecimal(Integer.parseInt(valveOpenStr), 2);
        } else if (e.getProtocolType().equals(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER)) {
            //如果采用海龙马阀门协议
            if (valveOpen.compareTo(new BigDecimal(100)) == 0) {
                valveOpenStr = "0001";
            } else {
                String v = String.valueOf(valveOpen);
                if (v.length() == 1) {
                    valveOpenStr = "0" + v + "00";
                } else if (v.length() == 2) {
                    valveOpenStr = v + "00";
                }
            }
            valveOpenStr = "01" + valveOpenStr;
        }

        //将valveOpen转为高低位的十六进制
        commandPo.setType(AgreementConstants.InstructionType.SEND);
//        valveOpenStr = HexadecimalUtil.hexConvertToHighLow(FormatData.tenToHexadecimal(valveOpen.intValue(), 2));

        commandPo.setInstructType("3");
        AgreementCommand command = agreementCommandService.getProtocolInstruction(commandPo);
        instruction.setInstructType(command.getInstructType());
        instruction.setInstructName(command.getInstructName());
        instruction.setPlotId(e.getLotId());
        instruction.setBuildId(e.getBuildId());
        instruction.setUnitId(e.getUnitId());
        instruction.setHouseholdId(e.getHouseholdId());
        instruction.setAddress(e.getAddress());
        String instructionSet = command.getPrefix() + command.getStartChar() + command.getDeviceType() + e.getAddress() +
                command.getControlCode() + command.getLengthField() + command.getDataFieldInstruct()
                + command.getDataMark() + valveOpenStr;

        //获取校验码
        String checkCode = HexadecimalUtil.getCheckCode(instructionSet);
        instructionSet += checkCode + command.getEndMark();
        instruction.setInstructionSet(instructionSet);
        return instruction;
    }

    /**
     * 组装供回水平均温度指令
     *
     * @param equipmentInfoVo 设备
     */
    private DeviceInstruction createAverageAndReturnWater(EquipmentInfoVo equipmentInfoVo, DataSetDto dto, List<Integer> modelIds, Map<String, Object> map) {
        BigDecimal value = BigDecimal.ZERO;
        String valveOpenStr = "";
        BigDecimal valveOpen;
        AgreementCommandPo commandPo = new AgreementCommandPo();
        AgreementHeatAvgPo po = new AgreementHeatAvgPo();
        po.setModelIds(modelIds);
        Household household;
        BuildUnit unit;
        DeviceInstruction instruction = new DeviceInstruction();
        instruction.setDtuId(equipmentInfoVo.getDtuId());
        Integer open;
        //最大阀门开度
        BigDecimal maxValveOpen = new BigDecimal("100");
        //最小阀门开度（即关断值）
        BigDecimal minValveOpen = new BigDecimal("30");

        BigDecimal stepValue = new BigDecimal(String.valueOf(dto.getStepValue()));

        //接收到的供回水平均温度
        BigDecimal avgValue = new BigDecimal(map.get("returnWaterTemperature").toString()).add(new BigDecimal(map.get("waterSupplyTemperature").toString())).divide(new BigDecimal("2"), 2, BigDecimal.ROUND_HALF_UP);

        //如果设备是单元，则获取和该单元同模型的供回水温度平均值
        //如果设备是住户，则获取和该住户同模型的供回水温度平均值
        Equipment e = new Equipment();
        if (equipmentInfoVo.getProtocolType().equals(AgreementConstants.AgreementType.UNIT_HEAT_METER)) {
            po.setProtocolType(AgreementConstants.AgreementType.UNIT_HEAT_METER);
            commandPo.setProtocolType(AgreementConstants.AgreementType.UNIT_VALVE);
            instruction.setProtocolType(AgreementConstants.AgreementType.UNIT_HEAT_METER);
            instruction.setProtocolName(AgreementConstants.AgreementTypeName.UNIT_VALVE);
            unit = unitService.getById(equipmentInfoVo.getUnitId());
            open = unit.getValveOpen() == null ? 0 : unit.getValveOpen();
            valveOpen = new BigDecimal(open);

            e = equipmentService.getOne(Wrappers.<Equipment>lambdaQuery()
                    .eq(Equipment::getUnitId, equipmentInfoVo.getUnitId())
                    .eq(Equipment::getProtocolType, AgreementConstants.AgreementType.UNIT_VALVE));

            instruction.setProtocolType(AgreementConstants.AgreementType.UNIT_VALVE);
            instruction.setProtocolName(AgreementConstants.AgreementTypeName.UNIT_VALVE);

        } else {
            //判断住户是户用阀门协议还是海龙马阀门协议
            List<Equipment> equipmentList = equipmentService.list(Wrappers.<Equipment>lambdaQuery()
                    .eq(Equipment::getHouseholdId, equipmentInfoVo.getHouseholdId())
                    .and((w) -> {
                        w.eq(Equipment::getProtocolType, AgreementConstants.AgreementType.HOUSEHOLD_VALVE)
                                .or().eq(Equipment::getProtocolType, AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                    }));

            if (equipmentList.isEmpty()) {
                return null;
            }
            e = equipmentList.get(0);
            po.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_HEAT_METER);
            if (e.getProtocolType().equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)) {
                commandPo.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_VALVE);
                instruction.setProtocolType(AgreementConstants.AgreementType.HOUSEHOLD_VALVE);
                instruction.setProtocolName(AgreementConstants.AgreementTypeName.HOUSEHOLD_VALVE);
            } else {
                commandPo.setProtocolType(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                instruction.setProtocolType(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                instruction.setProtocolName(AgreementConstants.AgreementTypeName.SEAHORSE_VALVE_METER);
            }

            household = householdService.getById(equipmentInfoVo.getHouseholdId());
            open = household.getValveOpen() == null ? 0 : household.getValveOpen();
            valveOpen = new BigDecimal(open);
        }


        //获取供回水温度平均值
        value = agreementHeatService.getAverageAndReturnWaterValue(po);

        if (avgValue.compareTo(value) != 0) {
            //如果当前回水温度小于最小值,则需要开大开度
            if (avgValue.compareTo(value) < 0) {
                if (valveOpen.add(stepValue).compareTo(maxValveOpen) > -1) {
                    valveOpen = maxValveOpen;
                } else {
                    valveOpen = valveOpen.add(stepValue);
                }
            }
            //如果当前回水温度大于最大值,则需要关小开度
            if (avgValue.compareTo(value) > 0) {
                if (valveOpen.subtract(stepValue).compareTo(minValveOpen) < 1) {
                    valveOpen = minValveOpen;
                } else {
                    valveOpen = valveOpen.subtract(stepValue);
                }
            }

        }
        //因为开度值的系数为0.1，即读的时候要除以10，那么设置的时候就要乘以10
        if (e.getProtocolType().equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)) {
            //如果采用户用阀门协议
            valveOpenStr = valveOpen.multiply(new BigDecimal(10)).toString();
            valveOpenStr = FormatData.tenToHexadecimal(valveOpen.intValue(), 2);
        } else if (e.getProtocolType().equals(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER)) {
            //如果采用海龙马阀门协议
            if (valveOpen.compareTo(new BigDecimal(100)) == 0) {
                valveOpenStr = "0001";
            } else {
                String v = String.valueOf(valveOpen);
                if (v.length() == 1) {
                    valveOpenStr = "0" + v + "00";
                } else if (v.length() == 2) {
                    valveOpenStr = v + "00";
                }
            }
            valveOpenStr = "01" + valveOpenStr;
        }

        commandPo.setInstructType("3");
        AgreementCommand command = agreementCommandService.getProtocolInstruction(commandPo);
        instruction.setInstructType(command.getInstructType());
        instruction.setInstructName(command.getInstructName());
        instruction.setPlotId(equipmentInfoVo.getLotId());
        instruction.setBuildId(e.getBuildId());
        instruction.setUnitId(e.getUnitId());
        instruction.setHouseholdId(e.getHouseholdId());
        instruction.setAddress(e.getAddress());
        String instructionSet = command.getPrefix() + command.getStartChar() + command.getDeviceType() + e.getAddress() +
                command.getControlCode() + command.getLengthField() + command.getDataFieldInstruct()
                + command.getDataMark() + valveOpenStr;
        //获取校验码
        String checkCode = HexadecimalUtil.getCheckCode(instructionSet);
        instructionSet += checkCode + command.getEndMark();
        instruction.setInstructionSet(instructionSet);
        return instruction;
    }


    /**
     * 查询初始设置详情
     *
     * @param po 查询初始设置dto
     * @return 初始设置
     */
    public DataSetListVo getDataSetInfo(DataSetPo po) {
        DataSetListDto dto = new DataSetListDto();
        BeanUtils.copyProperties(po, dto);
        LambdaQueryWrapper<DataSet> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DataSet::getType, po.getType());
        wrapper.eq(dto.getPlotId() != null, DataSet::getPlotId, dto.getPlotId());
        wrapper.eq(dto.getBuildId() != null, DataSet::getBuildId, dto.getBuildId());
        wrapper.eq(dto.getUnitId() != null, DataSet::getUnitId, dto.getUnitId());
        wrapper.eq(StringUtils.isNotEmpty(dto.getType()), DataSet::getType, dto.getType());
        wrapper.orderByDesc(DataSet::getId);
        DataSet dataSet = dataSetMapper.selectOne(wrapper);
        DataSetListVo vo = new DataSetListVo();
        if (dataSet == null) {
            dataSet = new DataSet();
        }
        BeanUtils.copyProperties(dataSet, vo);
        return vo;
    }

    /**
     * 根据下发的指令获取他的维度在初始设置（或平衡调控里的设置）
     * 返回实体类目前使用初始设置的实体类，但是也可能代表是平衡调控里的设置（因为2个实体类是一样的），所以不能以返回哪个实体类来判断是哪个设置
     *
     * @param deviceInstruction 下发的指令
     * @return 初始设置集合
     */
    public DataSet getDataSetByDeviceInstruction(DeviceInstruction deviceInstruction) {
        DataDevicePo po = new DataDevicePo();
        DataSet dataSet = null;
        //1：通过下发指令里的协议类型判断初始是哪个维度的（是小区、还是楼栋还是单元等）
        String protocolType = deviceInstruction.getProtocolType();
        if (protocolType.equals(AgreementConstants.AgreementType.UNIT_VALVE) ||
                protocolType.equals(AgreementConstants.AgreementType.UNIT_HEAT_METER)) {
            po.setType(AgreementConstants.DataType.BUILD_UNIT);
            po.setId(deviceInstruction.getUnitId());
            po.setTableName("build_unit");
        } else if (protocolType.equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE) ||
                protocolType.equals(AgreementConstants.AgreementType.HOUSEHOLD_HEAT_METER) ||
                protocolType.equals(AgreementConstants.AgreementType.SEAHORSE_VALVE_METER)
        ) {
            po.setType(AgreementConstants.DataType.HOUSEHOLD);
            po.setId(deviceInstruction.getHouseholdId());
            po.setTableName("c_household");
        }

        //2:通过组装的po查询维度下的设置类型
        DataDeviceVo deviceVo = this.getSetType(po);
        if (deviceVo == null) {
            return dataSet;
        }
        String setLevel = deviceVo.getSetLevel();

        //3:通过设置类型查询对应的初始设置。如果setType为1，则查询初始设置，如果为2，则查询平衡调控里的设置
        if (deviceVo.getSetType().equals(1)) {
            po.setTableName("data_set");
            LambdaQueryWrapper<DataSet> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(DataSet::getType, deviceVo.getSetLevel());
            if (setLevel.equals(AgreementConstants.DataType.PLOT)) {
                wrapper.eq(DataSet::getPlotId, deviceInstruction.getPlotId());
            } else if (setLevel.equals(AgreementConstants.DataType.BUILD)) {
                wrapper.eq(DataSet::getBuildId, deviceInstruction.getBuildId());
            } else {
                //如果是单元和住户，那么都用单元的设置
                wrapper.eq(DataSet::getUnitId, deviceInstruction.getUnitId());
            }
            dataSet = this.getOne(wrapper);

        } else {
            po.setTableName("control_set");
            LambdaQueryWrapper<ControlSet> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(ControlSet::getType, deviceVo.getSetLevel());
            if (setLevel.equals(AgreementConstants.DataType.PLOT)) {
                wrapper.eq(ControlSet::getPlotId, deviceInstruction.getPlotId());
                wrapper.groupBy(ControlSet::getPlotId).last(" limit 1");
            } else if (setLevel.equals(AgreementConstants.DataType.BUILD)) {
                wrapper.eq(ControlSet::getBuildId, deviceInstruction.getBuildId());
                wrapper.groupBy(ControlSet::getBuildId).last(" limit 1");
            } else if (setLevel.equals(AgreementConstants.DataType.BUILD_UNIT)) {
                wrapper.eq(ControlSet::getUnitId, deviceInstruction.getUnitId());
                wrapper.groupBy(ControlSet::getUnitId).last(" limit 1");
            } else {
                wrapper.eq(ControlSet::getHouseholdId, deviceInstruction.getHouseholdId());
                wrapper.groupBy(ControlSet::getHouseholdId).last(" limit 1");
            }
            ControlSet controlSet = controlSetService.getOne(wrapper);
            if (controlSet == null) {
                return dataSet;
            }
            dataSet = new DataSet();
            BeanUtils.copyProperties(controlSet, dataSet);
        }
        return dataSet;
    }

    /**
     * 根据维度id获取维度的设置类型（初始设置还是平衡调控）
     *
     * @param po 参数
     * @return 设置类型
     */
    private DataDeviceVo getSetType(DataDevicePo po) {
        return this.baseMapper.getSetType(po);
    }


}
